package services

import (
	"encoding/json"
	"github.com/pkg/errors"
	"go.didapinche.com/foundation/apisix-dashboard/internal/app/models"
	"go.didapinche.com/foundation/apisix-dashboard/internal/app/repository"
	"go.didapinche.com/foundation/apisix-dashboard/internal/pkg/utils"
	"go.etcd.io/etcd/clientv3"
	"go.uber.org/zap"
	"strings"
	"time"
)

type UpstreamService interface {
	Get(key, env string) (*models.Upstream, error)
	List(name, env string) ([]*models.Upstream, error)
	Create(upstream *models.Upstream, env string) error
	Update(upstream *models.Upstream, env string) error
	Delete(key, env string) error
	GetName(name string, env string) (bool, error)
	Syn(upstream *models.Upstream, env []string) error
}

type upstreamService struct {
	etcd   repository.EtcdRepository
	client map[string]*clientv3.Client
	log    *zap.Logger
}

func NewUpstreamService(
	etcd repository.EtcdRepository,
	client map[string]*clientv3.Client,
	log *zap.Logger,
) UpstreamService {
	return &upstreamService{
		etcd:   etcd,
		client: client,
		log:    log,
	}
}

func (s upstreamService) Get(key, env string) (*models.Upstream, error) {
	param, err := s.etcd.Get(s.client[env], "/apisix/upstreams/"+key)
	if err != nil {
		return nil, errors.Wrap(err, "call etcd.Get(s.client failed")
	}
	u := new(models.Upstream)
	err = json.Unmarshal([]byte(param), &u)
	if err != nil {
		return nil, errors.Wrap(err, "call json.Unmarshal failed")
	}
	return u, nil
}

func (s upstreamService) List(name, env string) ([]*models.Upstream, error) {
	params, err := s.etcd.List(s.client[env], "/apisix/upstreams/")
	if err != nil {
		return nil, errors.Wrap(err, "call etcd.Get failed")
	}
	us := make([]*models.Upstream, 0)
	for i := 0; i < len(params); i++ {
		u := new(models.Upstream)
		err = json.Unmarshal([]byte(params[i]), &u)
		if err != nil {
			s.log.Error("上游解析失败")
		} else {
			if name != "" && strings.Contains(u.Name, name) {
				us = append(us, u)
			}
			if name == "" {
				us = append(us, u)
			}
		}
	}
	return us, nil
}

func (s upstreamService) Create(upstream *models.Upstream, env string) error {
	if upstream.ID == "" {
		upstream.ID = utils.GetFlakeUidStr()
	}
	upstream.CreateTime = time.Now().Unix()
	upstream.UpdateTime = time.Now().Unix()
	str, err := json.Marshal(upstream)
	if err != nil {
		return errors.Wrap(err, "call json.Marshal failed")
	}
	if err := s.etcd.Create(s.client[env], "/apisix/upstreams/"+upstream.ID, string(str)); err != nil {
		return errors.Wrap(err, "call etcd.Create failed")
	}
	return nil
}

func (s upstreamService) Update(upstream *models.Upstream, env string) error {
	upstream.CreateTime = time.Now().Unix()
	upstream.UpdateTime = time.Now().Unix()
	str, err := json.Marshal(upstream)
	if err != nil {
		return errors.Wrap(err, "call json.Marshal failed")
	}
	if err := s.etcd.Update(s.client[env], "/apisix/upstreams/"+upstream.ID, string(str)); err != nil {
		return errors.Wrap(err, "call etcd.Update failed")
	}
	return nil
}

func (s upstreamService) Delete(key, env string) error {
	if err := s.etcd.Delete(s.client[env], "/apisix/upstreams/"+key); err != nil {
		return errors.Wrap(err, "call etcd.Delete failed")
	}
	return nil
}

func (s upstreamService) GetName(name, env string) (bool, error) {
	params, err := s.List(name, env)
	if err != nil {
		return true, errors.Wrap(err, "call upstreamService.List failed")
	}
	if len(params) > 0 {
		return true, errors.Wrap(err, "call upstreamService.List failed")
	}
	return false, nil
}

func (s upstreamService) Syn(upstream *models.Upstream, env []string) error {
	for _, e := range env {
		if err := s.Create(upstream, e); err != nil {
			return errors.Wrap(err, "call upstreamService.create error")
		}
	}
	return nil
}
